#pragma once

#include <boost/json.hpp>
#include <unistd.h>

#include <memory>
#include <string>
#include <vector>
#include <string_view>
#include <unordered_map>

#include <boost/date_time.hpp>

class DBValue
{
    friend class DBRow;
public:
    // template <typename T>
    // operator T() const;
    // {
    //     std::cout << "type:" << typeid(T).name() << std::endl;
    //     return T();
    // }
    operator double() const;
    operator float() const;
    operator int() const;
    operator bool() const;
    operator uint32_t() const;
    operator const char*() const;
    operator std::string() const;
    operator std::string_view() const;
    operator boost::gregorian::date() const;
    operator boost::posix_time::ptime() const;
private:
    DBValue(std::string_view value);
private:
    std::string_view value_;
};

// std::string &operator=(std::string &target, const DBValue &dbvalue)
// {
// }


class DBRow
{
    friend class DBQueryResult;
    friend class MysqlQueryResult;
    friend void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, DBRow const &obj);
public:
    DBValue operator[](std::string_view column_name) const;
    DBValue operator()(std::string_view column_name, std::string_view default_value) const;
private:
    DBRow(const std::unordered_map<std::string, int> &column_index, std::vector<std::string_view> &&row);
private:
    const std::unordered_map<std::string, int> &column_index_;
    const std::vector<std::string_view> row_;
};

void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, DBRow const &obj);


class DBQueryResult
{
public:
    virtual const std::vector<std::string_view> &column_names() const = 0;
    // virtual int column_index(std::string_view column_name) const = 0;
    virtual int row_count() const = 0;
    virtual const std::vector<DBRow> &result() const = 0;
protected:
    std::vector<std::string_view> fields_;
    std::vector<DBRow> result_;
    std::unordered_map<std::string, int> column_index_;
};

class DBException : public std::exception
{
    static std::unordered_map<int, const char*> error_msgs_;
public:
    DBException(const char *msg)
        : msg_(msg)
    {}
    // DBException(int error_no)
    //     : msg_(error_msgs_[error_no])
    // {
    // }
    virtual char const *what() const noexcept override
    {
        return msg_.c_str();
    }
protected:
    std::string msg_;
};


class DBClient
{
public:
    virtual void query(std::string_view sql) = 0;
    virtual ~DBClient() {}
    virtual const std::shared_ptr<DBQueryResult> result() const = 0;
protected:
};

inline bool is_main_thread()
{
    return getpid() == gettid();
}

